﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BattleOfColors.Control;

namespace BattleOfColors.Model
{
    /// <summary>
    /// Classe responsável por representar os blocos da matriz do mapa.
    /// Devem conter um soldado, ou um cheiro, ou um item, ou nada.
    /// </summary>
    public class Tile
    {
        #region atributos das coordenadas
        private int _x;

        public int X
        {
            get { return _x; }
            set { _x = value; }
        }
        private int _y;

        public int Y
        {
            get { return _y; }
            set { _y = value; }
        }

        private int _i;

        public int I
        {
            get { return _i; }
            set { _i = value; }
        }

        private int _j;

        public int J
        {
            get { return _j; }
            set { _j = value; }
        }
        #endregion

        private Smell smell;

        public Smell Smell
        {
            get { return smell; }
            set { smell = value; }
        }

        Soldier _positionedSoldier;

        public Soldier PositionedSoldier
        {
            get { return _positionedSoldier; }
            set { _positionedSoldier = value; }
        }

        //TODO futuramente adicionar item

        /// <summary>
        /// Construtor default do tile.
        /// X e Y representam as coordenadas espaciais do tile.
        /// I e J representam as coordenadas na matriz do tile para agilizar o acesso.
        /// </summary>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="I"></param>
        /// <param name="J"></param>
        public Tile(int X, int Y, int I, int J)
        {
            this.X = X;
            this.Y = Y;

            this.I = I;
            this.J = J;

            Smell = null;
            PositionedSoldier = null;
        }

        /// <summary>
        /// Retorna verdadeiro caso o cheiro do tile seja diferente de nulo
        /// </summary>
        /// <returns></returns>
        public bool HaveAnySmell()
        {
            return Smell != null;
        }

        /// <summary>
        /// Adiciona um cheiro no tile caso não exista nenhum.
        /// Caso exista, o cheiro será substituído se for mais forte, ou seja
        /// se tiver tempo remanescente maior que o atual.
        /// </summary>
        /// <param name="newSmell"></param>
        public void NewSmell(Smell newSmell)
        {
            if (HaveAnySmell())
            {
                if (Smell.RemaingTime < newSmell.RemaingTime)
                {
                    Smell = newSmell;
                }
            }
            else
            {
                Smell = newSmell;
            }
        }

        /// <summary>
        /// Chama o update dos cheiros no tile e do soldado posicionado naquele tile
        /// </summary>
        public void Update()
        {
            //Atualiza o cheiro da posição
            if (Smell != null)
            {
                Smell.Update();
                if (!Smell.StillUp())
                {
                    Smell = null;
                }
            }

            //Limpa soldados mortos
            if (PositionedSoldier != null && PositionedSoldier.Life <= 0)
            {
                PositionedSoldier = null;
            }
        }

        /// <summary>
        /// Verifica se a posição passada está contida no intervalo válido do campo de batalha
        /// </summary>
        /// <param name="I"></param>
        /// <param name="J"></param>
        /// <returns></returns>
        public static bool AvailableTile(int I, int J)
        {
            return I >= 0 && I < Specification.Instance.Dimension && J > 0 && J < Specification.Instance.Dimension;
        }

        /// <summary>
        /// Verifica se o movemento, dado a posição, é válido
        /// </summary>
        /// <param name="movement"></param>
        /// <param name="I"></param>
        /// <param name="J"></param>
        /// <returns></returns>
        public static bool AvailableMovement(Movement movement, int I, int J)
        {
            switch (movement)
            {
                case Movement.UP: return AvailableTile(I, J - 1);
                case Movement.DOWN: return AvailableTile(I, J + 1);
                case Movement.LEFT: return AvailableTile(I - 1, J);
                case Movement.RIGHT: return AvailableTile(I + 1, J);
            }

            return false;
        }

        /// <summary>
        /// Retorna a direção oposta à do tile
        /// </summary>
        /// <returns></returns>
        public static Movement OpposedTile(Movement movement)
        {
            switch (movement)
            {
                case Movement.UP: return Movement.DOWN;
                case Movement.DOWN: return Movement.UP;
                case Movement.LEFT: return Movement.RIGHT;
                case Movement.RIGHT: return Movement.LEFT;
            }

            return Movement.NULL;
        }

        /// <summary>
        /// Método para realizar a cópia profunda de um tile
        /// </summary>
        /// <returns></returns>
        public Tile CloneTile()
        {
            Tile clone = new Tile(X, Y, I, J);

            if (Smell != null)
            {
                clone.Smell = Smell;
            }

            if (PositionedSoldier != null)
            {
                clone.PositionedSoldier = PositionedSoldier.cloneSoldier();
                clone.PositionedSoldier.Location = clone;
            }

            return clone;
        }
    }
}
